﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Configuration;
using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace GetQQGroupMember
{
    public partial class Form1 : Form
    {
        private string cookie = "";
        private string gc = "";
        private string bkn = "";
        private string httpurl = "";
        private string resultStr = "";
        private int pageSt = 0;
        private int pageEnd = 40;
        string rStr = "";
        private static Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        public Form1()
        {
            InitializeComponent();
            //初始化时，为webbrowser的url赋值，访问页面
            webBrowser1.Url = new Uri(ConfigurationManager.AppSettings["loginurl"]);
            //qq群成果获取接口地址
            httpurl = ConfigurationManager.AppSettings["httpurl"];
            //要获取成员的群号
            textBox_qqNum.Text = ConfigurationManager.AppSettings["gc"];
        }

        /// <summary>
        /// 获取字符串指定位置的字符
        /// </summary>
        /// <param name="s">源字符串</param>
        /// <param name="index">索引位置</param>
        /// <returns>某个字符</returns>
        private string CharAt(string s, int index)
        {
            if ((index >= s.Length) || (index < 0))
                return "";
            return s.Substring(index, 1);
        }

        private void btn_start_Click(object sender, EventArgs e)
        {
            //每次点击初始化查询参数以及结果
            pageSt = 0;
            pageEnd = 40;
            rStr = "";
            //判断目标群号是否为空
            if (string.IsNullOrEmpty(textBox_qqNum.Text))
            {
                MessageBox.Show("QQ群号不能为空！");
                return;
            }
            //不为空，赋值qq群号
            gc = textBox_qqNum.Text;
            //如果config中存储的群号与当前群号不一致，那么更新它
            if (!gc.Equals(ConfigurationManager.AppSettings["gc"]))
            {
                config.AppSettings.Settings["gc"].Value = gc;
                config.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection("appSettings");
            }
            //判断webbrowser是否有cookie，即使未登陆，也可能存在cookie
            if (webBrowser1.Document!=null&&webBrowser1.Document.Cookie != null)
            {
                //获取已存在的cookie
                cookie = webBrowser1.Document.Cookie;
                //Console.WriteLine("cookieStr:" + cookie);
                //解析cookie
                string[] cookstr = cookie.Split(';');
                foreach (string str in cookstr)
                {
                    //通过计算，解析skey值得到bkn特征值
                    if (str.Trim().IndexOf("skey") == 0)
                    {
                        string[] cookieNameValue = str.Split('=');
                        //Console.WriteLine("skey--" + cookieNameValue[1]);
                        //以下算法从web js中获取，不同的qq登陆站点可能需要的参数并不一致
                        if (!string.IsNullOrEmpty(cookieNameValue[1]))
                        {
                            int t = 5381;
                            for (int r = 0, n = cookieNameValue[1].Length; r < n; ++r)
                            {
                                t += (t << 5) + (CharAt(cookieNameValue[1], r).ToCharArray()[0] & 0xff);
                                //Console.WriteLine(t + ",");
                            }
                            bkn = (2147483647 & t).ToString();
                            //Console.WriteLine("btn--" + btn);
                            break;
                        }
                    }
                }
                //获取所有群成员qq号
                resultStr = Get(httpurl,pageSt,pageEnd);
                textBox_qqs.Text = resultStr;
                //判断是否输出到文件
                if (checkBox_outFile.Checked)
                {
                    File.WriteAllText(textBox_filePath.Text, resultStr);
                    //将存储地址保存在config中，此处并没有什么用处，暂留
                    config.AppSettings.Settings["saveFilePath"].Value = textBox_filePath.Text;
                    config.Save(ConfigurationSaveMode.Modified);
                    ConfigurationManager.RefreshSection("appSettings");
                }
            }
        }
        //获取qq号的方法
        private string GetQQs(string jsons)
        {
            //Root是对qq群号接口返回json的解析的bean对象，反序列化json
            Root root = JsonConvert.DeserializeObject<Root>(jsons);
            //在此处判断是否登录了
            if (root.mems == null)
            {
                MessageBox.Show("登录信息错误,请重新登录！");
                return "";
            }
            //遍历json对象，拼接qq号字符串
            if (int.Parse(root.count) < pageEnd + 1)
            {
                for (int j = 0; j < root.mems.Count - 1; j++)
                {
                    rStr += root.mems[j].uin + ",";
                }
                rStr += root.mems[root.mems.Count - 1].uin;
            }
            else
            {
                for (int j = 0; j < root.mems.Count; j++)
                {
                    rStr += root.mems[j].uin + ",";
                }
            }
            //因为该接口每次只能获取41个qq号，需要判断是否已经到最后一批，如果不是那么递归调用，重新获取
            if (int.Parse(root.count) > pageEnd+1)
            {
                pageSt = pageEnd+1;
                pageEnd += 41;
                //Console.WriteLine("st-{0},end-{1}",pageSt,pageEnd);
                Get(httpurl, pageSt, pageEnd);
            }
            return rStr;
        }
        /// <summary>
        /// httpClinet -Get 方法
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private string Get(string url,int st, int end)
        {
            string result = "";
            string qqs = "";
            url += string.Format("?gc={0}&st={1}&end={2}&sort=0&bkn={3}", gc,st,end,bkn);
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "GET";
            req.Headers["Accept-Language"] = "zh-CN,zh;q=0.9";
            req.Referer = "http://qun.qq.com/member.html";
            req.Headers["cookie"] = cookie;
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            Stream stream = resp.GetResponseStream();
            try
            {
                //获取内容 
                using (StreamReader reader = new StreamReader(stream))
                {
                    result = reader.ReadToEnd();
                }
            }
            finally
            {
                stream.Close();
            }
            if (!string.IsNullOrEmpty(result))
            {
                qqs += GetQQs(result);
            }
            
            return qqs;
        }

        private void checkBox_outFile_Click(object sender, EventArgs e)
        {
            //选中输出到文件
            if (checkBox_outFile.Checked)
            {
                // 点击按钮，打开文件夹选择框
                OpenFileDialog ofd = new OpenFileDialog();
                //限定保存为json文件
                ofd.Filter = "TXT文件(*.txt)|*.txt";
                //获取选择的文件夹选择框，并将路径赋值给 textbox
                //选择了文件就赋值，否则取消选中状态
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    textBox_filePath.Text = ofd.FileName;
                }
                else
                {
                    checkBox_outFile.Checked = false;
                }
            }
        }
    }
    public class Lv
    {
        /// <summary>
        /// 
        /// </summary>
        public string point { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string level { get; set; }
    }

    public class MemsItem
    {
        /// <summary>
        /// 
        /// </summary>
        public string uin { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string role { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string flag { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string g { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string join_time { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string last_speak_time { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public Lv lv { get; set; }
        /// <summary>
        ///
        /// </summary>
        public string nick { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string card { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string qage { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string tags { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string rm { get; set; }
    }

    public class Root
    {
        /// <summary>
        /// 
        /// </summary>
        public string ec { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string errcode { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string em { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string adm_num { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string adm_max { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string vecsize { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<MemsItem> mems { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string count { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string svr_time { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string max_count { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string search_count { get; set; }
    }
}
